var express = require('express'),
  swig = require('swig-templates'),
  request = require('request'),
  router = express.Router();

// 因为在 app.js 没有使用 var 定义 config 变量，所以所有模块内都可以使用此变量。

var wx = {
  verify: function(req, res, callback) {
    if (req.cookies.user_id) {
      if (callback) callback();
    } else {
      res.redirect("/");
    }
  },
  // 向用户显示网页授权页面
  goLogin: function(req, res) {
    var url = "http://wechat.zfpresent.com";
    res.redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + config.appid + "&redirect_uri=" + encodeURIComponent(url) + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect");
  },

  // 在下面的 getToken 内调用；
  // 调用 jsapi 需要的；
  getGetticket: function(req, callback) {
    // 已有 ticket，但是过期了；
    if (config.getticket && (new Date().getTime()) > config.getticket.maxAge) {
      config.getticketReques = false;
    }
    if (config.getticketReques != true) {
      config.getticketReques = true;
      config.getticket = null;
      request.get({
        url: "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + config.serveToken.access_token + "&type=jsapi"
      }, function(error, response, body) {
        /*
        {
          "errcode":0,
          "errmsg":"ok",
          "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
          "expires_in":7200
        }
         */
        var bodyData = {
          noncestr: config.appid + "hvs" + Math.ceil(Math.random() * 100000),
          timestamp: new Date().getTime()
        };
        try {
          var data = JSON.parse(body),
            date = new Date();
          // 13 位毫秒级时间戳；
          data.maxAge = date.setSeconds(date.getSeconds() + data.expires_in);
          for (var i in data) {
            bodyData[i] = data[i];
          }
          bodyData.ticketStr = "jsapi_ticket=" + bodyData.ticket + "&noncestr=" + bodyData.noncestr + "&timestamp=" + bodyData.timestamp + "&url=";
          bodyData.appid = config.appid;
          config.getticket = JSON.parse(JSON.stringify(bodyData));
          /*
          {
            noncestr,
            timestamp,
            maxAge,
            ticketStr, 生成 signature 用；sha1(jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value)
            appid,
            errcode,
            errmsg,
            ticket,
            expire_in
          }
           */
        } catch (e) {
          config.getticketReques = false;
          bodyData = {
            errcode: 400
          };
        }
        if (callback) callback(bodyData);
      });
    } else if (config.getticketReques && !config.getticket) {
      // var invtTime = setInterval(function() {
      //   if (config.getticket) {
      //     if (callback) callback();
      //     clearInterval(invtTime);
      //   }
      // }, 100);
    } else {
      if (callback) callback();
    }
  },

  // 因为要使用公众号的功能，所以要获取公众号的 access_token（这个 token 是和用户无关的）；
  getToken: function(req, callback, gocallback) {
    if (config.serveToken && (new Date().getTime()) > config.serveToken.maxAge) {
      config.serveTokenReques = false;
    }
    if (config.serveTokenReques != true) {
      config.serveTokenReques = true;
      config.serveToken = null;
      request.get({
        url: "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + config.appid + "&secret=" + config.secret
      }, function(error, response, body) {
        // {"access_token":"ACCESS_TOKEN","expires_in":7200}
        try {
          var data = JSON.parse(body);
          if (data.access_token) {
            var date = new Date();
            data.maxAge = date.setSeconds(date.getSeconds() + data.expires_in);
            config.serveToken = data;
          } else {
            config.serveTokenReques = false;
          }
        } catch (e) {
          config.serveTokenReques = false;
        }
        if (gocallback) gocallback();
        wx.getGetticket(req, function() {
          if (callback) callback();
        });
      });
    } else if (config.serveTokenReques && !config.serveToken) {
      var invtTime = setInterval(function() {
        if (config.serveToken) {
          wx.getGetticket(req, function() {
            if (callback) callback();
          });
          if (gocallback) gocallback();
          clearInterval(invtTime);
        }
      }, 100);
    } else {
      if (gocallback) gocallback();
      wx.getGetticket(req, function() {
        if (callback) callback();
      });
    }
  },

  // 两处调用：
  // 要么token未过期
  // 要么刚从微信服务器接口中获取 access_token（这个 token 是和用户相关的）
  // 所以这里无需再做判断；
  // 把用户数据存放 session 中，key 是 wxUser;
  // 请求PHP接口，接口存储用于微信数据到数据库，返回 user_id；得到 user_id 存放到 cookie 中。
  getUser: function(req, res, callback) {
    var me = this;
    // try {
    request.get({
      url: "https://api.weixin.qq.com/sns/userinfo?access_token=" + req.session.tokenData.access_token + "&openid=" + req.session.tokenData.openid + "&lang=zh_CN "
    }, function(error, response, body) {
      var data = JSON.parse(body);
      req.session.wxUser = data;
      request.post({
        url: config.resourcePath.apiRoot + "/index/index/wechat",
        formData: {
          openid: data.openid,
          nickname: data.nickname,
          headimgurl: data.headimgurl,
          sex: data.sex
        }
      }, function(usererror, userresponse, userbody) {
        var userData = JSON.parse(userbody);
        req.session.user = userData;
        if (userData && userData.message) {
          res.cookie('user_id', userData.message.user_id, {
            maxAge: config.cookieTime,
            httpOnly: false
          });
        }
        if (typeof callback == 'function') {
          callback({
            code: 200,
            data: userData
          });
        }
      });
    });
    // } catch (e) {
    //     if (typeof callback == 'function') {
    //         callback({
    //             code: 201
    //         })
    //     }
    // }
  },

  // 请求微信服务器的 access_token（这个 token 是和用户相关的） 接口，获取 access_token，并存放在 session 中；
  // 两次调用：一个是获取 access_token，另一个是 access_token 过期了使用 refresh_token 来刷新 access_token；
  getAccessToken: function(req, res, url, callback) {
    var me = this;
    try {
      request.get({
        url: url
      }, function(error, response, body) {
        var data = JSON.parse(body);
        if (data.access_token) {
          var date = new Date();
          // access_token 的过期时间 maxAge 正是过期的时间点（Date对象）
          // 但这里，经过测试，结果其实是 13 位的毫秒级时间戳；
          // var datee = new Date();
          // console.log(datee.setSeconds(datee.getSeconds() + 7200)); 得到 1531203144383；
          // Number.MAX_VALUE => 1.7976931348623157e+308
          // Number.MAX_SAFE_INTEGER => 9007199254740991(16位)
          data.maxAge = date.setSeconds(date.getSeconds() + data.expires_in);
          req.session.tokenData = data;
          me.getUser(req, res, callback);
        } else {
          // 请求 access_token 的接口，获取到的数据中没有 access_token，出错了，重新授权；
          // code 只能用一次，请求重新请求 access_token时，重新发起微信授权是没有问题的；
          // 但是如果是刷新 access_token，最好是不要发起重新授权了，而是直接调用接口；暂时不管它了，先这样。
          req.session.wxCode = '';
          wx.goLogin(req, res);
          // 用户访问 http://wechat.zfpresent.com，执行 goAccessToken，
        }
      });
    } catch (e) {
      if (typeof callback == 'function') {
        callback({
          code: 201
        })
      }
    }
  },
  // 第一次执行时，执行最后的 else，显示授权页面；
  //    用户授权后，自动跳转到 http://wechat.zfpresent.com，于是第二次执行此方法；
  // 第二次执行时，url 中含有 code，所以执行中间的 else if；
  //    code 放入 session，key 是 wxCode；
  //    第一次执行这里时，session 中没有 tokenData，所以执行 else，执行 getAccessToken()；
  //        getAccessToken 中，
  //            如果接口有返回 access_token，把token和过期时间存放到 session，key 是 tokenData；调用 getUser();
  //              如果正常获取到数据，把用户的微信数据存放到 session 中，key 是 wxUser;
  //                  然后请求PHP接口，传递微信数据，得到user_id，把它存放到 cookie 中；
  //              代码中没有考虑到失败的情况，应该要处理才对~
  //            如果接口没有返回 access_token，
  //              如果是根据 code 来获取的，因为只能用一次，有效期只有5分钟，所以需要重新授权；
  //              如果是根据 refresh_token 来刷新 access_token，则可以直接递归调用自己；
  //  之后，用户再来访问 http://wechat.zfpresent.com，假如 user_id 这个 cookie 没有过期的话，就会执行 if 了。
  goAccessToken: function(req, res, callback) {
    var me = this;
    try {
      // 如果 cookie 中已有有效的 user_id，直接执行回调函数 callback；
      if (req.cookies.user_id && parseInt(req.cookies.user_id) > 0) {
        if (typeof callback == 'function') {
          callback({
            code: 200,
            data: req.session.wxUser
          });
        }
      } else if (req.query.code || req.session.wxCode) {
        // 如果 cookie 中没有有效的 user_id；
        // 但是请求 url 中有 code（用户授权后的跳转url中有此参数），
        // 或者 session 中有 wxCode（用户授权并跳转后，把 code 存放在 session中，key就是 wxCode）；
        if (!req.session.wxCode) {
          req.session.wxCode = req.query.code;
        }
        // 刷新token
        // session 中有 tokenData（access_token）
        if (req.session.tokenData) {
          // 当前时间大于 session 中的 access_token（这个 token 是和用户相关的） 的最大有效时间 maxAge （13位毫秒级时间戳），access_token 过期了，用 refresh_token 刷新 access_token；
          if ((new Date().getTime()) > req.session.tokenData.maxAge) {
            var link = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + config.appid + "&grant_type=refresh_token&refresh_token=" + req.session.tokenData.refresh_token;
            // 获取 access_token，并且内部调用 getUser(req, res, callback);
            me.getAccessToken(req, res, link, callback);
          } else {
            // 还没有过期，则使用此 token 获取用户信息；
            me.getUser(req, res, callback);
          }
        } else {
          // session 中没有 tokenData，说明还没有请求过 access_token；
          // 用户第一次进去应用创建token
          
          var link = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + config.appid + "&secret=" + config.secret + "&code=" + req.session.wxCode + "&grant_type=authorization_code";
          me.getAccessToken(req, res, link, callback);
        }
      } else {
        // 如果 cookie 中没有有效的 user_id；向用户显示网页授权页面；
        wx.goLogin(req, res);
      }
    } catch (e) {
      if (typeof callback == 'function') {
        callback({
          code: 201
        })
      }
    }
  }
};

module.exports = wx;